#include <render\avatar.h>
#include <base\quaternion.h>

#include <openglut.h>

#include <iostream>

void avatar::key_up_pressed(bool pressed)    { key_up    = pressed; }
void avatar::key_down_pressed(bool pressed)  { key_down  = pressed; }
void avatar::key_left_pressed(bool pressed)  { key_left  = pressed; }
void avatar::key_right_pressed(bool pressed) { key_right = pressed; }

void
avatar::setStartLocation(double lat, double lng, float altitude_, int level) {

  modelInfoState *mis = GET_STATE(modelInfoState, streamingClient::instance().connection());
  vec2i size = mis->info().terr_tile_size;

  start_tileLocation2d = mercatorProjection::getTiled(geoLocation(lat, lng), level) * vec2d(size.x, size.y);
  tileLocation2d = mercatorProjection::getTiled(geoLocation(lat, lng), level) * vec2d(size.x, size.y);

  base_level = level;
  altitude = altitude_;

  translation = vec3f((float) (tileLocation2d.x - start_tileLocation2d.x), (float) (tileLocation2d.y - start_tileLocation2d.y), altitude);

  moveState *ms = GET_STATE(moveState, streamingClient::instance().connection());
  ms->setStartPosition(vec3f((float) start_tileLocation2d.x, (float) start_tileLocation2d.y, altitude));

  move_sig((float) tileLocation2d.x, (float) tileLocation2d.y, altitude);
}

bool 
avatar::keyPressed() {  
  return (key_up || key_down || key_left || key_right);
}

vec2d  
avatar::getTileLocation2d(int level) {
  vec2d inlevel = tileLocation2d / pow (2.0, base_level);
  inlevel = inlevel * pow(2.0, level);
  return inlevel;
}

vec2d  
avatar::getStartTileLocation2d(int level) {
  vec2d inlevel = start_tileLocation2d / pow (2.0, base_level);
  inlevel = inlevel * pow(2.0, level);
  return inlevel;
}

vec2i  
avatar::getTileLocation2i(int level) {
  vec2d inlevel = tileLocation2d / pow (2.0, base_level);
  inlevel = inlevel * pow(2.0, level);
  vec2i tile((int) floor(inlevel[0]), (int) floor(inlevel[1]));
  return tile;
}

geoLocation
avatar::getGeoLocation() {
  geoLocation geoloc = inverseMercatorProjection::getGEO(tileLocation2d, base_level);
  return geoloc;
}

vec3f 
avatar::getRotation() {
  return rotation;
}

vec3f 
avatar::getView() {
  return view;
}

vec3f 
avatar::getUp() {
  return up;
}

vec3f 
avatar::getTranslation() {
  return translation;
}

void 
avatar::changeAltitude(float diff) {
  altitude += diff;

  translation = vec3f((float) (tileLocation2d.x - start_tileLocation2d.x), (float) (tileLocation2d.y - start_tileLocation2d.y), altitude);
  vpstate->info.frustum.setCamDef(-translation, -translation - view, -up);

  move_sig((float) tileLocation2d.x, (float) tileLocation2d.y, altitude);
}

float 
avatar::getAltitude() {
  return altitude;
}

void 
avatar::applyRotation() { 
  glRotatef(rotation.x, 1.0, 0.0, 0.0);
  glRotatef(rotation.z, 0.0, 0.0, 1.0);
}

void 
avatar::setSpeed(float speed_) {
  speed = speed_;
}

void 
avatar::applyTranslation() { 
  glTranslatef(translation.x, translation.y, altitude);
}

void  
avatar::translate() {

  float movement_speed = MOVEMENT_SPEED / speed;

  if (key_up) {
    tileLocation2d.x += view.x * movement_speed;
    tileLocation2d.y += view.y * movement_speed;
  }

  if (key_down) {
    tileLocation2d.x -= view.x * movement_speed;
    tileLocation2d.y -= view.y * movement_speed;
  }

  if (key_left) {
    tileLocation2d.x += view.y * movement_speed;
    tileLocation2d.y -= view.x * movement_speed;
  }

  if (key_right) {
    tileLocation2d.x -= view.y * movement_speed;
    tileLocation2d.y += view.x * movement_speed;
  }

  if (keyPressed()) {
    
    translation = vec3f((float) (tileLocation2d.x - start_tileLocation2d.x), (float) (tileLocation2d.y - start_tileLocation2d.y), altitude);
    vpstate->info.frustum.setCamDef(-translation, -translation - view, -up);

    move_sig((float) tileLocation2d.x, (float) tileLocation2d.y, altitude);
  }

}

void 
avatar::rotate(float x, float y, float z) {
  rotation = vec3f(x, y, z);

  view = quaternion::rotate((rotation.x * PI) / 180.0, 1, 0, 0, vec3f(0, 1, 0));
  view = quaternion::rotate((rotation.z * PI) / 180.0, 0, 0, -1, view);

  up = quaternion::rotate((rotation.x * PI) / 180.0, 1, 0, 0, vec3f(0, 0, -1));
  up = quaternion::rotate((-rotation.z * PI) / 180.0, 0, 0, -1, up);

  vpstate->info.frustum.setCamDef(-translation, -translation - view, -up);

  rotate_sig(x, y, z);
}

void 
avatar::rotate(float dx, float dy) {
    
  // limit maximum rotation step
  if (dx > MAX_Z_ROTATE_STEP) dx = MAX_Z_ROTATE_STEP; else if (dx < -MAX_Z_ROTATE_STEP) dx = -MAX_Z_ROTATE_STEP;
  if (dy > MAX_X_ROTATE_STEP) dy = MAX_X_ROTATE_STEP; else if (dy < -MAX_X_ROTATE_STEP) dy = -MAX_X_ROTATE_STEP;

  rotation.z -= (100.0f / speed) * 0.2f * dx;
  rotation.x += (100.0f / speed) * 0.1f * dy;
  
  // limit up\down view
  if (rotation.x > MAX_X_ROTATE_ANGLE) rotation.x = MAX_X_ROTATE_ANGLE;
  if (rotation.x < MIN_X_ROTATE_ANGLE) rotation.x = MIN_X_ROTATE_ANGLE;

  // loop around axis
  if (rotation.z > 360.0f) rotation.z = rotation.z - 360.0f;
  if (rotation.z < 0.0f) rotation.z = rotation.z + 360.0f;

  view = quaternion::rotate((-rotation.x * PI) / 180.0, 1, 0, 0, vec3f(0, 1, 0));
  view = quaternion::rotate((rotation.z * PI) / 180.0, 0, 0, -1, view);

  up = quaternion::rotate((-rotation.x * PI) / 180.0, 1, 0, 0, vec3f(0, 0, -1));
  up = quaternion::rotate((rotation.z * PI) / 180.0, 0, 0, -1, up);

  vpstate->info.frustum.setCamDef(-translation, -translation - view, -up);

  rotate_sig(rotation.x, rotation.y, rotation.z);
}

void 
avatar::addMoveObserver(moveObserver *ref) {
  move_sig.connect(boost::bind(&moveObserver::move, ref, _1 , _2, _3));
}

void 
avatar::addRotateObserver(rotateObserver *ref) {
  rotate_sig.connect(boost::bind(&rotateObserver::rotate, ref, _1 , _2, _3));
}

avatar::avatar() : key_up(false), key_down(false), key_left(false), key_right(false), tileLocation2d(0.0, 0.0), rotation(0.0, 0.0, 0.0), altitude(0.0), speed(1.0) {
  vpstate = GET_STATE(viewingParametersState, streamingClient::instance().connection());
}

avatar::~avatar() {

}
